/* *
 * @file    audio_i2s.c
 * @author  Azolla (1228449928@qq.com)
 * @brief   ES7148 I2S audio code.
 * @version 0.1
 * @date    2022-11-04
 * 
 * @copyright Copyright (c) 2022
 * */
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include "driver/gpio.h"
#include "esp_system.h"
#include "esp_check.h"

#include "audio_i2s.h"

/** I2S 总线接口！
 * SCLK -> BCLK   (Serial Clock)：串行时钟线，也称位时钟(BCLK)，数字音频的每一位数据都对应有一个CK脉冲，它的频率为：2*采样频率*量化位数，2代表左右两个通道数据。
 * WS   -> LRCK   (Word Select)：字段选择线，也称帧时钟(LRC)线，表明当前传输数据的声道，不同标准有不同的定义。WS线的频率等于采样频率(FS)。
 * CLKIN-> MCLK   (Main Clock): 主时钟(MCK)
 * SDO  -> SDOUT  (Serial Data)：串行数据线，用于发送或接收两个时分复用的数据通道上的数据(仅半双工模式)，如果是全双工模式，该信号仅用于发送数据。
 * SDI  -> SDIN     
 */
 
/* I2S port and GPIOs */
#define I2S_NUM         (0)
 
#define I2S_MCK_IO      (GPIO_NUM_NC)        /* 0 From MCLK of esp32   1 From BCLK */
#define I2S_BCK_IO      (GPIO_NUM_36)
#define I2S_WS_IO       (GPIO_NUM_35)
#define I2S_DO_IO       (GPIO_NUM_37)
#define I2S_DI_IO       (GPIO_NUM_NC)
#define PA_CTR_IO       (GPIO_NUM_NC)
 
/* I2S configurations */
#define CONFIG_I2S_SAMPLE_RATE     (16000)
#define CONFIG_I2S_MCLK_MULTIPLE   I2S_MCLK_MULTIPLE_256  // If not using 24-bit data width, 256 should be enough

static const char *TAG = "audio_i2s";

static uint8_t audio_volume;

// set volume.
void audio_set_volume(uint8_t vol)
{
    audio_volume = vol > 100 ? 100 : vol;
}

#if 0
 
#include "driver/i2s.h"
#include "driver/adc.h"

// I2S built-in ADC unit
#define I2S_ADC_UNIT              ADC_UNIT_1
// I2S built-in ADC channel
#define I2S_ADC_CHANNEL           ADC1_CHANNEL_9  // ADC1_CHANNEL_9,     /*!< ADC1 channel 9 is GPIO10 */

void audio_i2s_clear_buffer(void)
{
    ESP_LOGI(TAG, "i2s_zero_dma_buffer");
    i2s_zero_dma_buffer(I2S_NUM);
}

static esp_err_t i2s_driver_init(void)
{
    i2s_config_t i2s_cfg = {
        .mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX | I2S_MODE_ADC_BUILT_IN,
        .sample_rate = CONFIG_I2S_SAMPLE_RATE,
        .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
        .channel_format  = I2S_CHANNEL_FMT_ONLY_RIGHT, //I2S_CHANNEL_FMT_ALL_RIGHT,
        .communication_format = I2S_COMM_FORMAT_STAND_I2S,
        .mclk_multiple = CONFIG_I2S_MCLK_MULTIPLE,
        .fixed_mclk = 0, /* If fixed_mclk and use_apll are set, use fixed_mclk as mclk frequency, otherwise calculate by mclk = sample_rate * multiple */
        .use_apll = true,
        .dma_buf_count = 8,
        .dma_buf_len = AUDIO_I2S_BUFF_SIZE,
        .tx_desc_auto_clear = true, 
        .intr_alloc_flags = ESP_INTR_FLAG_LEVEL6,
#if SOC_I2S_SUPPORTS_TDM
        .total_chan = 1,
        .chan_mask = I2S_TDM_ACTIVE_CH0,
        .left_align = false,
        .big_edin = false,
        .bit_order_msb = false,
        .skip_msk = false,
#endif
    };
    ESP_RETURN_ON_ERROR( i2s_driver_install(I2S_NUM, &i2s_cfg, 0, NULL), TAG, "install i2s failed");

    i2s_pin_config_t i2s_pin_cfg = {
        .mck_io_num = I2S_MCK_IO,
        .bck_io_num = I2S_BCK_IO,
        .ws_io_num  = I2S_WS_IO,
        .data_out_num = I2S_DO_IO,
        .data_in_num  = I2S_DI_IO
    };
    ESP_RETURN_ON_ERROR( i2s_set_pin(I2S_NUM, &i2s_pin_cfg), TAG, "set i2s pins failed");

    // 注意：需要根据音频设置合适的 采样率 和 通道， 不然没声音的！
    ESP_ERROR_CHECK( i2s_set_clk(I2S_NUM, i2s_cfg.sample_rate, i2s_cfg.bits_per_sample, I2S_CHANNEL_MONO) );

    //init ADC pad
    i2s_set_adc_mode(I2S_ADC_UNIT, I2S_ADC_CHANNEL);

    return ESP_OK;
}

void audio_i2s_adc_read(void *arg)
{
    int i2s_read_len = 16*1024;
    size_t bytes_read, bytes_total = 0;
    char *i2s_read_buff = (char*) calloc(i2s_read_len, sizeof(char));
    
    i2s_adc_enable(I2S_NUM);
   
    //read data from I2S bus, in this case, from ADC.
    i2s_read(I2S_NUM, (void*) i2s_read_buff, i2s_read_len, &bytes_read, portMAX_DELAY);
    bytes_total += bytes_read;
    i2s_adc_disable(I2S_NUM);
    free(i2s_read_buff);
    i2s_read_buff = NULL;
}

size_t audio_i2s_write(const void *src, size_t size)
{
#if 1  // 声音调节
    int16_t middle;
    uint8_t *buf = (uint8_t *)src;
    for (int i = 0; i < size / 2; i++) {
        middle = ((buf[2 * i]) | (buf[2 * i + 1] << 8));
        middle = middle * audio_volume / 100;
        buf[2 * i] = (middle) & 0xFF;
        buf[2 * i + 1] = (middle >> 8) & 0xFF;
    }
#endif
    size_t len = 0;
    esp_err_t ret = i2s_write(I2S_NUM, src, size, &len, portMAX_DELAY);
    if (ret != ESP_OK || len <= 0) {
        ret = 0;
    }
    return len;
}
#else 

#include "driver/i2s_std.h"
 
static i2s_chan_handle_t tx_handle = NULL;
static i2s_chan_handle_t rx_handle = NULL;

static esp_err_t i2s_driver_init(void)
{
    i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM, I2S_ROLE_MASTER);
    chan_cfg.auto_clear = true; // Auto clear the legacy data in the DMA buffer
    ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
    i2s_std_config_t std_cfg = {
        .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(CONFIG_I2S_SAMPLE_RATE),
        .slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
        .gpio_cfg = {
            .mclk = I2S_MCK_IO,
            .bclk = I2S_BCK_IO,
            .ws = I2S_WS_IO,
            .dout = I2S_DO_IO,
            .din = I2S_DI_IO,
            .invert_flags = {
                .mclk_inv = false,
                .bclk_inv = false,
                .ws_inv = false,
            },
        },
    };
 
    ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
    ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));
    return ESP_OK;
}

size_t audio_i2s_write(const void *src, size_t size)
{
    if (audio_volume < 100) {  // 音量调节
        int16_t middle;
        uint8_t *buf = (uint8_t *)src;
        for (int i = 0; i < size / 2; i++) {
            middle = ((buf[2 * i]) | (buf[2 * i + 1] << 8));
            middle = middle * audio_volume / 100;
            buf[2 * i] = (middle) & 0xFF;
            buf[2 * i + 1] = (middle >> 8) & 0xFF;
        }
    }
    size_t len = 0;
    esp_err_t ret = i2s_channel_write(tx_handle, src, size, &len, portMAX_DELAY);
    if (ret != ESP_OK || len <= 0) {
        ret = 0;
    }
    return len;
}

#endif


#if 0
CMakeLists.txt add: set(COMPONENT_EMBED_TXTFILES POWER_ON.pcm)
extern const uint8_t audio_test_start[] asm("_binary_POWER_ON_pcm_start");
extern const uint8_t audio_test_end[]   asm("_binary_POWER_ON_pcm_end");
audio_i2s_write(&audio_test_start[0xC00], 50*1024);
#endif


void audio_i2s_init(void)
{
    /* Initialize i2s peripheral */
    if (i2s_driver_init() != ESP_OK) {
        ESP_LOGE(TAG, "i2s driver init failed");
        abort();
    }
    audio_set_volume(100);
 
#if PA_CTR_IO > 0
    gpio_config_t io_conf;
    //disable interrupt
    io_conf.intr_type = GPIO_INTR_DISABLE;
    //set as output mode
    io_conf.mode = GPIO_MODE_OUTPUT;
    //bit mask of the pins that you want to set,e.g. 
    io_conf.pin_bit_mask = BIT64(PA_CTR_IO);
    //disable pull-down mode
    io_conf.pull_down_en = 0;
    //disable pull-up mode
    io_conf.pull_up_en = 0;
    // configure GPIO with the given settings
    gpio_config(&io_conf);

    gpio_set_level(PA_CTR_IO, 1);   // OPA
#endif 
}
